package com.aliyun.inventory.server.service;

import java.util.Date;

import com.aliyun.commons.model.Result;
import com.aliyun.commons.service.CacheService;
import com.aliyun.inventory.client.constants.InventoryCacheKey;
import com.aliyun.inventory.client.model.InventoryDTO;
import com.aliyun.inventory.client.service.InventoryService;
import com.aliyun.inventory.server.dao.entity.InventoryDO;
import com.aliyun.inventory.server.dao.entity.InventoryDeductionDetailDO;
import com.aliyun.inventory.server.dao.mapper.InventoryDeductionDetailMapper;
import com.aliyun.inventory.server.dao.mapper.InventoryMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * 库存服务实现
 *
 * 
 * @date 2024/11/27
 */
@Slf4j
@DubboService
public class InventoryServiceImpl implements InventoryService {

    @Autowired
    private CacheService cacheService;
    @Autowired
    private InventoryMapper inventoryMapper;
    @Autowired
    private InventoryDeductionDetailMapper inventoryDeductionDetailMapper;

    @Override
    public Result<InventoryDTO> getInventory(long productId) {
        String key = InventoryCacheKey.buildInventoryKey(productId);
        InventoryDO inventoryDO = cacheService.getValue(key, InventoryDO.class);
        if (inventoryDO == null) {
            QueryWrapper<InventoryDO> queryWrapper = new QueryWrapper<>(InventoryDO.class).eq("product_id", productId);
            inventoryDO = inventoryMapper.selectOne(queryWrapper);
            if (inventoryDO != null) {
                // 永久缓存
                cacheService.setValue(key, inventoryDO, 0);
            }
        }
        return Result.success(toInventoryDTO(inventoryDO));
    }

    @Override
    public Result<Integer> getRemainQuantity(long productId) {
        String key = InventoryCacheKey.buildRemainQuantityKey(productId);
        // 正常情况下，应该是在秒杀商品上架时，就将库存信息写入缓存，而不是在这里初始化
        Integer remainQuantity = cacheService.getCounter(key);
        if (remainQuantity == null) {
            // 缓存中不存在，则从数据库中获取
            QueryWrapper<InventoryDO> queryWrapper = new QueryWrapper<>(InventoryDO.class).eq("product_id", productId);
            InventoryDO inventoryDO = inventoryMapper.selectOne(queryWrapper);
            if (inventoryDO == null) {
                return Result.fail("INVENTORY_NOT_FOUND", "找不到商品库存");
            }
            remainQuantity = inventoryDO.getRemainQuantity();
            // 设置计数器值，永久缓存
            cacheService.initCounter(key, inventoryDO.getRemainQuantity(), 0);
        }
        return Result.success(remainQuantity);
    }

    @Override
    public Result<Integer> getUserPurchasedQuantity(long productId, long userId) {
        String key = InventoryCacheKey.buildUserPurchasedQuantityKey(productId, userId);
        Integer userPurchasedQuantity = cacheService.getCounter(key);
        if (userPurchasedQuantity == null) {
            return Result.success(0);
        }
        return Result.success(userPurchasedQuantity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> deductInventory(long userId, long productId, int deductionQuantity) {
        // 检查商品库存
        InventoryDTO inventoryDTO = getInventory(productId).getData();
        if (inventoryDTO == null) {
            return Result.fail("INVENTORY_NOT_FOUND", "找不到商品库存");
        }

        // 校验
        Result<Void> fail = validate(userId, productId, deductionQuantity, inventoryDTO);
        if (fail != null) {
            return fail;
        }

        // 扣减库存
        int row = inventoryMapper.deductInventory(productId, deductionQuantity);
        if (row != 1) {
            return Result.fail("INVENTORY_NOT_ENOUGH", "库存不足");
        }

        // 记录扣减明细
        saveInventoryDeductionDetailDO(userId, productId, deductionQuantity, inventoryDTO);

        // 更新用户购买数量计数器
        incrementUserPurchasedCounter(userId, productId, deductionQuantity);

        return Result.success(null);
    }

    @Override
    public Result<Void> resetCache(long productId, long userId) {
        // 清除
        cacheService.deleteKey(InventoryCacheKey.buildInventoryKey(productId));
        cacheService.deleteKey(InventoryCacheKey.buildRemainQuantityKey(productId));
        cacheService.deleteKey(InventoryCacheKey.buildUserPurchasedQuantityKey(productId, userId));
        // 设置
        getInventory(productId);
        getRemainQuantity(productId);

        // 缓存中不存在，则从数据库中获取
        Integer userPurchasedQuantity = inventoryDeductionDetailMapper.getTotalDeductionQuantity(userId, productId);
        if (userPurchasedQuantity == null) {
            userPurchasedQuantity = 0;
        }
        String key = InventoryCacheKey.buildUserPurchasedQuantityKey(productId, userId);
        cacheService.initCounter(key, userPurchasedQuantity, 0);
        return Result.success(null);
    }

    /**
     * 校验
     *
     * @param userId            用户 ID
     * @param productId         商品 ID
     * @param deductionQuantity 抵扣数量
     * @param inventoryDTO      清单 DTO
     * @return {@link Result }<{@link Void }>
     */
    private Result<Void> validate(long userId, long productId, int deductionQuantity, InventoryDTO inventoryDTO) {
        // 每个用户限购检查
        Integer maxPurchasePerUser = inventoryDTO.getMaxPurchasePerUser();
        if (deductionQuantity > maxPurchasePerUser) {
            return Result.fail("QUANTITY_EXCEED_MAX_PURCHASE_PER_USER", "购买数量超过最大购买限制");
        }

        // 检查当前用户扣减的数量是否超过最大购买限制
        Integer totalDeductionQuantity = inventoryDeductionDetailMapper.getTotalDeductionQuantity(userId, productId);
        if (totalDeductionQuantity == null) {
            totalDeductionQuantity = 0;
        }
        if (totalDeductionQuantity + deductionQuantity > maxPurchasePerUser) {
            return Result.fail("USER_PURCHASE_LIMIT_EXCEEDED", "用户超过最大购买限制");
        }
        return null;
    }

    /**
     * 用户已购买数量计数器增加
     *
     * @param userId            用户 ID
     * @param productId         商品 ID
     * @param deductionQuantity 抵扣数量
     */
    private void incrementUserPurchasedCounter(long userId, long productId, int deductionQuantity) {
        // 事务提交后执行
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                try {
                    cacheService.incrementCounter(InventoryCacheKey.buildUserPurchasedQuantityKey(productId, userId),
                        deductionQuantity);
                } catch (Exception e) {
                    // 忽略异常，这里即使更新计数器异常，redis未写成功，也不会有致命的影响
                    log.error("update user purchased counter failed, productid={} userid={} message={}", productId,
                        userId, e.getMessage(), e);
                }
            }
        });
    }

    /**
     * save库存演绎细节实体
     *
     * @param userId            用户 ID
     * @param productId         商品 ID
     * @param deductionQuantity
     * @param inventoryDTO      清单 DTO
     */
    private void saveInventoryDeductionDetailDO(long userId, long productId, int deductionQuantity,
        InventoryDTO inventoryDTO) {
        InventoryDeductionDetailDO detailDO = new InventoryDeductionDetailDO();
        detailDO.setUserId(userId);
        detailDO.setProductId(productId);
        detailDO.setInventoryId(inventoryDTO.getId());
        detailDO.setDeductionQuantity(deductionQuantity);
        detailDO.setGmtCreate(new Date());
        inventoryDeductionDetailMapper.insert(detailDO);
    }

    /**
     * 转换 DTO
     *
     * @param inventoryDO 库存实体
     * @return {@link InventoryDTO }
     */
    private InventoryDTO toInventoryDTO(InventoryDO inventoryDO) {
        if (inventoryDO == null) {
            return null;
        }
        InventoryDTO inventoryDTO = new InventoryDTO();
        inventoryDTO.setId(inventoryDO.getId());
        inventoryDTO.setGmtCreate(inventoryDO.getGmtCreate());
        inventoryDTO.setProductId(inventoryDO.getProductId());
        inventoryDTO.setTotalQuantity(inventoryDO.getTotalQuantity());
        inventoryDTO.setRemainQuantity(inventoryDO.getRemainQuantity());
        inventoryDTO.setMaxPurchasePerUser(inventoryDO.getMaxPurchasePerUser());
        return inventoryDTO;
    }
}